/**
 * Copyright 2019 吉鼎科技.

 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package cn.easyplatform.engine.cmd.task;

import cn.easyplatform.EasyPlatformWithLabelKeyException;
import cn.easyplatform.contexts.ListContext;
import cn.easyplatform.contexts.WorkflowContext;
import cn.easyplatform.dos.FieldDo;
import cn.easyplatform.dos.LogDo;
import cn.easyplatform.dos.Record;
import cn.easyplatform.engine.runtime.PageTaskSupport;
import cn.easyplatform.engine.runtime.RuntimeTask;
import cn.easyplatform.engine.runtime.RuntimeTaskFactory;
import cn.easyplatform.engine.runtime.datalist.DataListUtils;
import cn.easyplatform.engine.runtime.page.PageTask;
import cn.easyplatform.entities.BaseEntity;
import cn.easyplatform.entities.beans.bpm.BpmBean;
import cn.easyplatform.entities.beans.task.TaskBean;
import cn.easyplatform.interceptor.AbstractCommand;
import cn.easyplatform.interceptor.CommandContext;
import cn.easyplatform.lang.Strings;
import cn.easyplatform.messages.request.BeginRequestMessage;
import cn.easyplatform.messages.response.PageResponseMessage;
import cn.easyplatform.messages.response.SimpleResponseMessage;
import cn.easyplatform.messages.vos.FxTaskVo;
import cn.easyplatform.messages.vos.MeshTaskVo;
import cn.easyplatform.messages.vos.PageVo;
import cn.easyplatform.messages.vos.TaskVo;
import cn.easyplatform.messages.vos.datalist.ListTaskVo;
import cn.easyplatform.support.scripting.BreakPoint;
import cn.easyplatform.type.*;
import cn.easyplatform.util.EntityUtils;
import cn.easyplatform.util.MessageUtils;
import cn.easyplatform.util.RuntimeUtils;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.SetUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * @author <a href="mailto:davidchen@epclouds.com">littleDog</a> <br/>
 * @since 2.0.0 <br/>
 */
public class BeginCmd extends AbstractCommand<BeginRequestMessage> {

    /**
     * @param req
     */
    public BeginCmd(BeginRequestMessage req) {
        super(req);
    }

    @Override
    public IResponseMessage<?> execute(CommandContext cc) {
        if (req.getActionFlag() < 0) {
            cc.setBreakPoint(null);
            cc.removeWorkflowContext();
            return new SimpleResponseMessage();
        }
        BreakPoint bp = cc.getBreakPoint();
        if (bp == null) {
            TaskVo tv = req.getBody();
            TaskBean task = cc.getEntity(tv.getId());
            if (task == null)
                return MessageUtils.entityNotFound(EntityType.TASK.getName(),
                        tv.getId());
            String refId = task.getRefId();
            if (!Strings.isBlank(refId) && refId.startsWith("$")
                    && cc.getWorkflowContext() != null)
                refId = (String) cc.getWorkflowContext().getRecord()
                        .getValue(refId.substring(1));
            if (Strings.isBlank(refId))
                return MessageUtils.entityNotFound(
                        EntityType.PAGE.getName(), task.getRefId());
            if (refId.startsWith("~./")) {// 表示自定义页面
                WorkflowContext ctx = createContext(cc, task, tv);
                cc.setWorkflowContext(ctx);
                PageVo pv = new PageVo();
                pv.setId(ctx.getId());
                pv.setPage(refId);
                pv.setTitile(ctx.getParameterAsString("811"));
                pv.setImage(ctx.getParameterAsString("813"));
                pv.setOpenModel(ctx.getParameterAsInt("805"));
                pv.setProcessCode(ctx.getParameterAsString("814"));
                pv.setVisible(ctx.getParameterAsBoolean("816"));
                return new PageResponseMessage(pv);
            }
            BaseEntity base = cc.getEntity(refId);
            if (base == null)
                return MessageUtils.entityNotFound(EntityType.PAGE.getName(),
                        task.getRefId());
            BpmBean bb = null;
            if (base instanceof BpmBean) {// 工作流
                bb = (BpmBean) base;
                String taskId = cc.getBpmEngine().getInstanceTask(bb.getId());
                if (taskId == null)
                    return MessageUtils.entityNotFound(
                            EntityType.TASK.getName(), taskId);
                task = cc.getEntity(taskId);
                if (task == null)
                    return MessageUtils.entityNotFound(
                            EntityType.TASK.getName(), taskId);
                task = EntityUtils.getInheritTask(cc, null, task);
                base = cc.getEntity(task.getRefId());
                if (base == null)
                    return MessageUtils.entityNotFound(
                            EntityType.PAGE.getName(), task.getRefId());
            } else
                task = EntityUtils.getInheritTask(cc, null, task);
            task = task.clone();
            task.setName(RuntimeUtils.getLabel(cc, task.getName()));
            if (!Strings.isBlank(tv.getProcessCode()))
                task.setProcessCode(tv.getProcessCode());
            RuntimeTask runtime = RuntimeTaskFactory.createRuntime(base
                    .getType());
            WorkflowContext host = cc.getWorkflowContext();
            WorkflowContext ctx = createContext(cc, task, tv);
            cc.setWorkflowContext(ctx);
            if (bb != null)
                RuntimeUtils.initBpm(ctx, bb);
            IResponseMessage<?> resp = runtime.doTask(cc, base);
            if (!resp.isSuccess()) {
                char c = resp.getCode().toLowerCase().charAt(0);
                if (c != 'c' && c != 'w')
                    cc.removeWorkflowContext();
            }
            if (cc.getUser().getLogLevel() >= LogDo.LEVEL_TASK)
                RuntimeUtils.log(cc, LogDo.TYPE_TASK, "begin", tv.getId());
            //如果是页面检查
            if (resp.getBody() instanceof PageVo && ((PageVo) resp.getBody()).isVisible()) {
                String power = ctx.getParameterAsString("841");
                if (Strings.isBlank(power) && !Strings.isBlank(ctx.getParameterAsString("806")))
                    power = cc.getIdentityDao(false).getAccess(ctx.getParameterAsString("806"), tv.getId(), cc.getEnv().getDeviceType().getName());
                Set<String> set = new HashSet<>();
                if (!Strings.isBlank(power)) {
                    for (String tag : power.split(","))
                        set.add(tag);
                }
                if (host != null && !Strings.isBlank((power = host.getParameterAsString("841")))) {
                    for (String tag : power.split(","))
                        set.add(tag);
                }
                if (set.isEmpty()) {
                    ctx.setParameter("841", "");
                } else {
                    String[] access = new String[set.size()];
                    set.toArray(access);
                    power = ArrayUtils.toString(access);
                    ((PageVo) resp.getBody()).setAccess(access);
                    ctx.setParameter("841", StringUtils.substringBetween(power, "{", "}"));
                }
            }
            return resp;
        } else {
            // 只有PageBean才支持断点
            PageTaskSupport ps = new PageTask();
            IResponseMessage<?> resp = ps.doPageBreakPoint(cc, bp);
            if (!resp.isSuccess()) {
                char c = resp.getCode().toLowerCase().charAt(0);
                if (c != 'c' && c != 'w')
                    cc.removeWorkflowContext();
            }
            return resp;
        }
    }

    private WorkflowContext createContext(CommandContext cc, TaskBean task,
                                          TaskVo tv) {
        // 从已存在的功能触发,在这里就需要摸拟流程,在当前的功能上增加上一个功能信息,以便Task.INIT逻辑做映射的处理
        WorkflowContext host = cc.getWorkflowContext();
        WorkflowContext ctx = null;
        if (tv instanceof ListTaskVo) {
            if (tv.getOpenModel() == Constants.OPEN_EMBBED)
                task.setSubType("");
            ListTaskVo lv = (ListTaskVo) tv;
            ListContext lc = host.getList(lv.getListId());
            if (lv.getKeys() == null) {
                ctx = new WorkflowContext(task, tv.getOpenModel(),
                        lc.getRecord(lv.getKeys()), null);
            } else if (lc.getType().equals(Constants.DETAIL)) {
                String key = lc.lock(cc, lv.getKeys());
                ctx = new WorkflowContext(task, tv.getOpenModel(),
                        lc.getRecord(lv.getKeys()), key);
            } else {
                if (lc.isCustom()) {
                    FieldDo[] data = DataListUtils.getRow(cc, lc, lv.getKeys());
                    Record record = new Record();
                    for (int i = 0; i < data.length; i++)
                        record.set(data[i]);
                    ctx = new WorkflowContext(task, tv.getOpenModel(),
                            lc.createRecord(record));
                } else {
                    String key = lc.lock(cc, lv.getKeys());
                    ctx = new WorkflowContext(task, tv.getOpenModel(),
                            lc.createRecord(DataListUtils.getRecord(cc, lc,
                                    lv.getKeys())), key);
                }
            }
        } else if (tv instanceof MeshTaskVo) {
            Record record = new Record();
            for (FieldVo fv : tv.getVariables())
                record.set(RuntimeUtils.castTo(fv));
            ctx = new WorkflowContext(task, tv.getOpenModel(), host.createRecord(record), null);
        } else if (tv instanceof FxTaskVo) {
            FxTaskVo ftv = (FxTaskVo) tv;
            host = cc.getWorkflowContext(ftv.getSessionId(), ftv.getWorkflowId());
            if (host == null)
                throw new EasyPlatformWithLabelKeyException("context.session.not.found", ftv.getId());
            return new WorkflowContext(task, tv.getOpenModel(), host.getRecord(), null);
        } else {
            if (host == null)
                ctx = new WorkflowContext(task, tv);
            else {
                if (Strings.isBlank(tv.getFrom())) {
                    ctx = new WorkflowContext(task, tv.getOpenModel(),
                            host.getRecord());
                } else {// 来源于其它层
                    if (NumberUtils.isNumber(tv.getFrom())) {
                        int layer = NumberUtils.toInt(tv.getFrom());
                        ctx = new WorkflowContext(task, tv.getOpenModel(),
                                host.getRecordContext(layer));
                    } else if (tv.getFrom().indexOf("->") > 0) {
                        // 分层下的EMBBED数据源
                        int pos = tv.getFrom().indexOf("->");
                        int layer = NumberUtils.toInt(tv.getFrom().substring(0,
                                pos));
                        String cid = tv.getFrom().substring(pos + 2);
                        String id = host.getTask(layer, cid);
                        if (id == null)
                            throw new EasyPlatformWithLabelKeyException(
                                    "task.not.found.from.layer", layer, id);
                        WorkflowContext wc = cc.getWorkflowContext(id);
                        if (wc == null)
                            throw new EasyPlatformWithLabelKeyException(
                                    "task.not.found.from.layer", layer, id);
                        ctx = new WorkflowContext(task, tv.getOpenModel(),
                                wc.getRecord());
                    } else {
                        // 当前层其它EMBBED数据源
                        String id = host.getTask(tv.getFrom());
                        if (id == null)
                            throw new EasyPlatformWithLabelKeyException(
                                    "task.not.found.from.layer",
                                    host.getParameter("828"), id);
                        WorkflowContext wc = cc.getWorkflowContext(id);
                        if (wc == null)
                            throw new EasyPlatformWithLabelKeyException(
                                    "task.not.found.from.layer",
                                    host.getParameter("828"), id);
                        ctx = new WorkflowContext(task, tv.getOpenModel(),
                                wc.getRecord());
                    }
                }
            }
        }
        if (host != null)
            host.appendChild(cc,
                    tv.getCid() == null ? tv.getId() : tv.getCid(), ctx);
        return ctx;
    }

    @Override
    public String getName() {
        return "task.Begin";
    }
}
